#include "Win32Proactor.h"
#include "Win32AsynchIO.h"
#include "Trace.h"

Win32Proactor::Win32Proactor ()
: completion_port_ (0) // This *MUST* be 0, *NOT* NDK_INVALID_HANDLE !!!
, number_of_threads_ (0)
{
    TRACE ("");
}
Win32Proactor::~Win32Proactor ()
{
    this->close ();
}
int Win32Proactor::open (size_t number_of_threads/* = 0*/)
{
    if (number_of_threads > 0)
    {
        this->number_of_threads_ = static_cast<DWORD> (number_of_threads);
    }else
    {
        this->number_of_threads_ = NDK::get_cpu_count ();
    }

    // Create the completion port.
    this->completion_port_ = ::CreateIoCompletionPort (INVALID_HANDLE_VALUE,
            0,
            0,
            this->number_of_threads_);
    if (this->completion_port_ == 0)
        return -1;
    return 0;
}
int Win32Proactor::register_handle (NDK_HANDLE handle,
        const void *completion_key,
        int /*event_mask*/)
{
    ULONG_PTR comp_key (reinterpret_cast<ULONG_PTR> (completion_key));
    // No locking is needed here as no state changes.
    NDK_HANDLE cp = ::CreateIoCompletionPort (handle,
            this->completion_port_,
            comp_key,
            this->number_of_threads_);
    if (cp == 0)
    {
        // If errno == ERROR_INVALID_PARAMETER, then this handle was
        // already registered.
        DWORD err = ::GetLastError ();
        if (err != ERROR_INVALID_PARAMETER)
        {
            return -1;
        }
    }
    return 0;
}
int Win32Proactor::handle_events (const TimeValue *timeout/* = 0*/)
{
    OVERLAPPED *overlapped = 0;
    u_long bytes_transferred = 0;
    ULONG_PTR completion_key = 0;

    unsigned long milli_seconds = INFINITE;
    if (timeout != 0)
        milli_seconds = timeout->msec ();	
    // Get the next asynchronous operation that completes
    BOOL result = ::GetQueuedCompletionStatus (this->completion_port_,
            &bytes_transferred,
            &completion_key,
            &overlapped,
            milli_seconds);
    if (result == FALSE && overlapped == 0)
    {
        DWORD err = ::GetLastError (); 
        if (err == WAIT_TIMEOUT)
            return 0;
        else if (ERROR_SUCCESS == err)
            return 0;
        else
            return -1;
    }else if (overlapped != 0)
    {
        // Narrow the result.
        Win32AsynchResult *asynch_result = (Win32AsynchResult *)overlapped;
        DWORD err = ::GetLastError ();
        asynch_result->complete (static_cast<size_t> (bytes_transferred),
                (void *) completion_keym,
                err);
    }
    return 1;
}
AsynchReadStreamImpl *Win32Proactor::create_asynch_read_stream (void) 
{
    TRACE ("");
    AsynchReadStreamImpl *impl= 0;
    impl = new Win32AsynchReadStream (this);
    return impl;
}
AsynchReadStreamResultImpl *Win32Proactor::create_asynch_read_stream_result (
        const AsynchHandler *handler,
        NDK_HANDLE handle, 
        MessageBlock *mb, 
        size_t bytes_to_read) 
{
    TRACE ("");
    AsynchReadStreamResultImpl *impl = 0;
    impl = new Win32AsynchReadStreamResult(handler,
            handle,
            mb,
            bytes_to_read);
    return impl;
}
AsynchWriteStreamImpl *Win32Proactor::create_asynch_write_stream (void) 
{
    TRACE ("");
    AsynchWriteStreamImpl *impl = 0;
    impl = new Win32AsynchWriteStream (this);
    return impl;
}
AsynchWriteStreamResultImpl *Win32Proactor::create_asynch_write_stream_result (
        const AsynchHandler *handler,
        NDK_HANDLE handle, 
        MessageBlock *mb, 
        size_t bytes_to_write) 
{
    TRACE ("");
    AsynchWriteStreamResultImpl *impl = 0;
    impl = new Win32AsynchWriteStreamResult(handler,
            handle,
            mb,
            bytes_to_write);
    return impl;
}
AsynchAcceptImpl *Win32Proactor::create_asynch_accept (void) 
{
    TRACE ("");
    AsynchAcceptImpl *impl = 0;
    impl = new Win32AsynchAccept (this);
    return impl;
}
AsynchAcceptResultImpl *Win32Proactor::create_asynch_accept_result (
        const AsynchHandler *handler,
        NDK_HANDLE listen_handle, 
        MessageBlock *mb, 
        size_t num_to_accept) 
{
    TRACE ("");
    AsynchAcceptResultImpl *impl = 0;
    impl = new Win32AsynchAcceptResult(handler,
            listen_handle,
            mb,
            num_to_accept);
    return impl;
}
